package com.nineclock.sign.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nineclock.common.entity.PageResult;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.filter.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.sign.dto.*;
import com.nineclock.sign.excel.CustomHandler;
import com.nineclock.sign.mapper.SignInfoMapper;
import com.nineclock.sign.mapper.SignMapper;
import com.nineclock.sign.mapper.SignPictureMapper;
import com.nineclock.sign.pojo.SignInfo;
import com.nineclock.sign.pojo.SignPicture;
import com.nineclock.sign.service.SignInfoService;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.feign.SysCompanyUserFeign;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class SignInfoServiceImpl implements SignInfoService {

    @Autowired
    private SignMapper signInfoMapper;

    @Autowired
    private SysCompanyUserFeign companyUserFeign;

    @Autowired(required = false)
    private SignPictureMapper signPictureMapper;


    @Autowired(required = false)
    private RedisTemplate redisTemplate;


    @Override
    public void insert(SignInfoDTO signInfoDTO) {
        if (signInfoDTO==null){
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }

        SignInfo signInfo = new SignInfo();

        Long companyId = CurrentUserHolder.get().getCompanyId();
        signInfo.setCompanyId(companyId);
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();
        signInfo.setCompanyUserId(companyUserId);

        signInfo.setCreateTime(new Date());
        signInfo.setLat(signInfoDTO.getLat());
        signInfo.setLng(signInfoDTO.getLng());
        signInfo.setAddress(signInfoDTO.getAddress());
        signInfo.setRemark(signInfoDTO.getRemark());
        signInfo.setVisit(signInfoDTO.getVisit());
        signInfo.setImei(signInfoDTO.getImei());

        //年月日
        String date = DateTimeUtil.dateToStr(new Date(), DateTimeUtil.TIME_FORMAT_2);
        signInfo.setCreateDate(date);

        //年月
        String month = DateTimeUtil.dateToStr(new Date(), DateTimeUtil.TIME_FORMAT_4);
        signInfo.setCreateMonth(month);

        signInfoMapper.insert(signInfo);

        //逗号切割图片列表
        String images = signInfoDTO.getImages();
        String[] spImages = images.split(",");

        for (String image : spImages) {
            SignPicture signPicture = new SignPicture();
            signPicture.setSignId(signInfo.getId());
            signPicture.setUrl(image);
            signPictureMapper.insert(signPicture);
        }

//        int dateSum  = 0;
//        redisTemplate.opsForValue().set("date"+companyUserId+date,dateSum++,);
//
//        int monthSum = 0;
//        redisTemplate.opsForValue().set("month"+companyUserId+month,monthSum++,);
    }
    //APP: 获取当前用户当天签到次数
    @Override
    public Integer getDayCount() {
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String format = sdf.format(date);
//        String format = "2021-03-21";
        Integer count = signInfoMapper.getCompanyUser(companyUserId,format);
        redisTemplate.opsForValue().set(CurrentUserHolder.get().getCompanyUserId(),count);

        return count;

    }

    /*@Override
    public List<SignInfoDTO> pagingSignCurrentUserByMonth() {
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();
        List<SignInfo> sign = getSign(companyUserId);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        //String format = sdf.format(new Date());
        String format = "2021-03";

        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCompanyUserId,companyUserId);
        wrapper.eq(SignInfo::getCreateMonth,format);

        List<SignInfo> signInfoList =  signMapper.selectList(wrapper);
        Integer integer = signMapper.selectCount(wrapper);
        String monthCount = integer.toString();
        LambdaQueryWrapper<SignPicture> wrapper1 = new LambdaQueryWrapper<>();

        List<SignInfoDTO> signInfoDToList = signInfoList.stream().map(signInfo -> {
            SignInfoDTO signInfoDTo = new SignInfoDTO();
            signInfoDTo.setCompanyId(CurrentUserHolder.get().getCompanyId());
            signInfoDTo.setCompanyUserId(companyUserId);
            signInfoDTo.setCreateDate(signInfo.getCreateDate());
            signInfoDTo.setCreateMonth(signInfo.getCreateMonth());

            signInfoDTo.setCreateTime(signInfo.getCreateTime());

            signInfoDTo.setDayCount(redisTemplate.opsForValue().get("companyUserId").toString());
            signInfoDTo.setImages("");
            signInfoDTo.setImei(signInfo.getImei());
            signInfoDTo.setLat(signInfo.getLat());
            signInfoDTo.setLng(signInfo.getLng());
            signInfoDTo.setMonthCount(monthCount);
            signInfoDTo.setRemark(signInfo.getRemark());
            signInfoDTo.setUserName(CurrentUserHolder.get().getUsername());
            signInfoDTo.setVisit(signInfo.getVisit());
            wrapper1.eq(SignPicture::getSignId, signInfo.getId());
            List<SignPicture> signPictureList = signPictureMapper.selectList(wrapper1);
            signInfoDTo.setSignPictureUrlList(signPictureList);
            return signInfoDTo;
        }).collect(Collectors.toList());

        return signInfoDToList;
    }

    //获取当前用户签到所有数据
    public List<SignInfo> getSign(Long companyUserId){
        List<SignInfo> signInfoList = signMapper.getSign(companyUserId);
        return signInfoList;
    }*/


    /**
     * 接口名称：APP: 查询本月签到记录
     * 接口路径：GET/sign/pagingSignCurrentUserByMonth
     */
    @Override
    public List<SignInfoDTO> pagingSignCurrentUserByMonth(String dateStr) {

        //参数校验
        if (StringUtils.isEmpty(dateStr)) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }

        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId());
        wrapper.eq(SignInfo::getCompanyUserId, CurrentUserHolder.get().getCompanyUserId());
        wrapper.eq(SignInfo::getCreateMonth, dateStr);
        wrapper.orderByAsc(SignInfo::getCreateTime);
        List<SignInfo> signInfoList = signInfoMapper.selectList(wrapper);
        if (!CollectionUtils.isEmpty(signInfoList)) {

            //封装属性信息
            List<SignInfoDTO> signInfoDTOList = getSignInfoDTOList(signInfoList);
            String dayCount = redisTemplate.opsForValue().get(CurrentUserHolder.get().getCompanyUserId()).toString();
            for (SignInfoDTO signInfoDTO : signInfoDTOList) {
                //封装当月次数
                signInfoDTO.setMonthCount(String.valueOf(signInfoList.size()));
                signInfoDTO.setDayCount(dayCount);
            }

            return signInfoDTOList;
        }
        return Arrays.asList();
    }



    /**
     * 接口名称：APP: 获取团队员工打卡列表
     * 接口路径：GET/sign/listSignAllUser
     */
    @Override
    public List<SignInfoAllDTO> listSignAllUser(String dateStr) {
        //参数校验
        if (StringUtils.isEmpty(dateStr)) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }
        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId());
        wrapper.eq(SignInfo::getCreateDate, dateStr);
        wrapper.orderByAsc(SignInfo::getCreateTime);
        List<SignInfo> signInfoList = signInfoMapper.selectList(wrapper);

        if (!CollectionUtils.isEmpty(signInfoList)) {
            //对公司员工进行分组
            Map<Long, List<SignInfo>> companyUserGroupMap =
                    signInfoList.stream().collect(Collectors.groupingBy(SignInfo::getCompanyUserId));
            //获取查询到的所有员工id
            Set<Long> companyUserIds = signInfoList.stream().map(SignInfo::getCompanyUserId).collect(Collectors.toSet());

            List<SignInfoAllDTO> signInfoAllDTOList = new ArrayList<>();
            for (Long companyUserId : companyUserIds) {
                SignInfoAllDTO signInfoAllDTO = new SignInfoAllDTO();
                //每一位员工当天的所有签到信息集合
                List<SignInfo> signInfoVeryUser = companyUserGroupMap.get(companyUserId);
                //获取每一位员工当天的签到次数
                String dayCount = String.valueOf(signInfoVeryUser.size());
                //封装其他信息,用户名、创建时间、url地址集合
                List<SignInfoDTO> signInfoDTOList = getSignInfoDTOList(signInfoVeryUser);
                //封装签到次数
                signInfoDTOList = signInfoDTOList.stream().map(signInfoDTO -> {
                    signInfoDTO.setDayCount(dayCount);
                    return signInfoDTO;
                }).collect(Collectors.toList());

                signInfoAllDTO.setSignResponseList(signInfoDTOList);
                //补全signInfoAllDTO中username信息
                signInfoAllDTO.setUsername(signInfoDTOList.get(0).getUserName());
                signInfoAllDTOList.add(signInfoAllDTO);
            }
            return signInfoAllDTOList;
        }
        return Arrays.asList();
    }



    /**
     * 封装属性信息
     *
     * @param signInfoList
     * @return
     */
    private List<SignInfoDTO> getSignInfoDTOList(List<SignInfo> signInfoList) {
        return signInfoList.stream().map(signInfo -> {
            //属性拷贝
            SignInfoDTO signInfoDTO = BeanHelper.copyProperties(signInfo, SignInfoDTO.class);
            //封装时间信息
            signInfoDTO.setCreateTime(DateTimeUtil.dateToStr(signInfo.getCreateTime(), DateTimeUtil.STANDARD_FORMAT));
            //封装用户名信息
            SysCompanyUserDTO companyUserDTO = companyUserFeign.queryCompanyUserById(signInfoDTO.getCompanyUserId()).getData();
            signInfoDTO.setUserName(companyUserDTO.getUserName());

            LambdaQueryWrapper<SignPicture> pictureWrapper = new LambdaQueryWrapper<>();
            pictureWrapper.eq(SignPicture::getSignId, signInfoDTO.getId());
            List<SignPicture> pictureList = signPictureMapper.selectList(pictureWrapper);
            if (!CollectionUtils.isEmpty(pictureList)) {
                //将地址url封装
                List<String> signPictureUrlList = new ArrayList<>();
                for (SignPicture signPicture : pictureList) {
                    signPictureUrlList.add(signPicture.getUrl());
                }
                signInfoDTO.setSignPictureUrlList(signPictureUrlList);
            }

            return signInfoDTO;

        }).collect(Collectors.toList());

    }



    /**
     * PC: 查询签到列表
     * @param startTime
     * @param endTime
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageResult<SignPageDTO> querySignInfoPage(String startTime, String endTime, Integer page, Integer pageSize) {
        //获取开始日期与结束日期的集合
        List<String> spanDateList = DateTimeUtil.getSpanDateList(startTime, endTime);

        //构架条件构造器，查询当前公司、在这段日期内的签到情况
        LambdaQueryWrapper<SignInfo> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId());
        wrapper.between(SignInfo::getCreateDate,startTime,endTime);

        List<SignInfo> signInfos = signInfoMapper.selectList(wrapper);

        //封装数据对象
        List<SignPageDTO> signPageDTOs = new ArrayList<>();
        if (!CollectionUtils.isEmpty(signInfos)) {

            //拿到以员工id为分组的数据
            Map<Long, List<SignInfo>> companyUserIdMap = signInfos.stream().collect(Collectors.groupingBy(SignInfo::getCompanyUserId));

            //拿到签到的员工id集合
            Set<Long> longs = companyUserIdMap.keySet();
            List<Long> ids=new ArrayList<>();
            for (Long conpanyUserId : longs) {
                Long id = companyUserIdMap.get(conpanyUserId).get(0).getId();
                ids.add(id);
            }

            //分页
            LambdaQueryWrapper<SignInfo> pagewrapper=new LambdaQueryWrapper<>();
            pagewrapper.in(SignInfo::getId,ids);
            IPage<SignInfo> ipage=new Page<>(page,pageSize);
            ipage= signInfoMapper.selectPage(ipage,pagewrapper);

            for (Long userid : companyUserIdMap.keySet()) {

                SignPageDTO signPageDTO = new SignPageDTO();

                //获取员工信息
                SysCompanyUserDTO companyUserDTO = companyUserFeign.queryCompanyUserById(userid).getData();

                //封装数据
                signPageDTO.setUsername(companyUserDTO.getUserName());
                signPageDTO.setPost(companyUserDTO.getPost());
                signPageDTO.setWorkNumber(companyUserDTO.getWorkNumber());
                signPageDTO.setId(companyUserDTO.getId());

                //设置部门信息
                SignDepartmentDTO signDepartmentDTO = new SignDepartmentDTO();
                signDepartmentDTO.setId(companyUserDTO.getDepartmentId());
                signDepartmentDTO.setName(companyUserDTO.getDepartmentName());
                signPageDTO.setDepartment(signDepartmentDTO);

                //获取员工这段时间的数据
                List<SignInfo> signInfoList = companyUserIdMap.get(userid);
                List<SignsDTO> signs = spanDateList.stream().map(date -> {

                    SignsDTO signsDTO = new SignsDTO();
                    signsDTO.setSignDate(date);

                    //获取以时间为分组的数据
                    Map<String, List<SignInfo>> map = signInfoList.stream().collect(Collectors.groupingBy(SignInfo::getCreateDate));

                    //获取当天数据
                    List<SignInfo> infoList = map.get(date);

                    if (!CollectionUtils.isEmpty(infoList)){
                        signsDTO.setSignCount(infoList.size());
                    }else {
                        signsDTO.setSignCount(0);
                    }

                    return signsDTO;

                }).collect(Collectors.toList());

                signPageDTO.setSigns(signs);

                signPageDTOs.add(signPageDTO);
            }

            PageResult<SignPageDTO> result = new PageResult(ipage.getTotal(), ipage.getPages(), signPageDTOs);
            return result;
        }

        return null;
    }

    /**
     * PC: 导出签到列表
     * @param startTime
     * @param endTime
     */
    @Override
    public void export(String startTime, String endTime) throws IOException {
        //根据传入的startTime、endTime，查询时间段内的签到数据
        List<SignPageDTO> signInfo = getSignInfo(startTime, endTime);

        // 2.获取响应对象response，设置文件下载的头信息;
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = servletRequestAttributes.getResponse();
        response.setHeader("Content-disposition", "attachment;filename=SignInfo.xlsx");
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setCharacterEncoding("utf-8");


        //3.通过response获取输出流, 并生成的Excel输出
        EasyExcel.write(response.getOutputStream())
                .excelType(ExcelTypeEnum.XLSX)
                .head(handleHeaders(signInfo)) // 构造表头信息
                .registerWriteHandler(horizontalCellStyleStrategy())
                .registerWriteHandler(new CustomHandler())
                .sheet("签到数据")
                .doWrite(handleData(signInfo)); // 构造每一行的数据
    }

    /**
     * 构造每一行数据
     * @param signInfo
     * @return
     */
    private List<List<String>> handleData(List<SignPageDTO> signInfo) {

        List<List<String>> dataList=new ArrayList<>();

        if (!CollectionUtils.isEmpty(signInfo)){

            // 遍历每一行数据
            for (SignPageDTO signPageDTO : signInfo) {

                // 构造每一行数据
                List<String> row=new ArrayList<>();
                row.add(signPageDTO.getUsername());// 姓名
                row.add(signPageDTO.getWorkNumber());// 工号
                row.add(signPageDTO.getDepartment().getName());// 部门
                row.add(signPageDTO.getPost());// 职位

                // 获取当前数据中每一天的签到状态
                List<SignsDTO> signs = signPageDTO.getSigns();

                if (!CollectionUtils.isEmpty(signs)){

                    for (SignsDTO sign : signs) {
                        row.add(sign.getSignCount().toString());
                    }
                }
                dataList.add(row);

            }
        }
        return dataList;
    }

    /**
     * 构造表头
     * @param signInfo
     * @return
     */
    private List<List<String>> handleHeaders(List<SignPageDTO> signInfo) {

        List<List<String>> headers=new ArrayList<>();
        headers.add(Arrays.asList("姓名"));
        headers.add(Arrays.asList("工号"));
        headers.add(Arrays.asList("部门"));
        headers.add(Arrays.asList("职位"));

        if (!CollectionUtils.isEmpty(signInfo)){
            List<SignsDTO> signs = signInfo.get(0).getSigns();

            for (SignsDTO sign : signs) {
                headers.add(Arrays.asList(sign.getSignDate()));
            }
        }
        return headers;

    }

    /**
     * 设置样式
     */
    public HorizontalCellStyleStrategy horizontalCellStyleStrategy() {

        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //内容策略 - 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        //设置字体
        WriteFont font = new WriteFont();
        font.setFontName("阿里巴巴普惠体");
        font.setFontHeightInPoints((short) 11);
        contentWriteCellStyle.setWriteFont(font);
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(contentWriteCellStyle, contentWriteCellStyle);

        return horizontalCellStyleStrategy;
    }

    /**
     * 根据传入的startTime、endTime，查询时间段内的签到数据
     * @param startTime
     * @param endTime
     * @return
     */
    private List<SignPageDTO> getSignInfo(String startTime, String endTime) {
        //获取开始日期与结束日期的集合
        List<String> spanDateList = DateTimeUtil.getSpanDateList(startTime, endTime);

        //查出数据
        LambdaQueryWrapper<SignInfo> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId());
        wrapper.between(SignInfo::getCreateDate, startTime, endTime);
        List<SignInfo> signInfos = signInfoMapper.selectList(wrapper);


        //封装数据对象
        List<SignPageDTO> signPageDTOs = new ArrayList<>();
        if (!CollectionUtils.isEmpty(signInfos)){
            //拿到当前页数据，拿到以员工id为分组的数据
            Map<Long, List<SignInfo>> map = signInfos.stream().collect(Collectors.groupingBy(SignInfo::getCompanyUserId));

            for (Long id : map.keySet()) {
                //封装
                SignPageDTO signPageDTO = new SignPageDTO();

                //获取员工数据
                SysCompanyUserDTO companyUserDTO = companyUserFeign.queryCompanyUserById(id).getData();

                //封装数据
                signPageDTO.setId(id);
                signPageDTO.setUsername(companyUserDTO.getUserName());
                signPageDTO.setPost(companyUserDTO.getPost());
                signPageDTO.setWorkNumber(companyUserDTO.getWorkNumber());

                SignDepartmentDTO signDepartmentDTO = new SignDepartmentDTO();
                signDepartmentDTO.setId(companyUserDTO.getDepartmentId());
                signDepartmentDTO.setName(companyUserDTO.getDepartmentName());

                signPageDTO.setDepartment(signDepartmentDTO);

                //获取当前员工在这段时间的所有签到数据
                List<SignInfo> infoList = map.get(id);

                List<SignsDTO> signs = spanDateList.stream().map(date -> {
                    SignsDTO signsDTO = new SignsDTO();
                    signsDTO.setSignDate(date);

                    //获取当前员工以日期分组的数据
                    Map<String, List<SignInfo>> dateMap = infoList.stream().collect(Collectors.groupingBy(SignInfo::getCreateDate));

                    //获取当天的签到数据
                    List<SignInfo> signInfoDateList = dateMap.get(date);
                    if (!CollectionUtils.isEmpty(signInfoDateList)) {
                        signsDTO.setSignCount(signInfoDateList.size());
                    } else {
                        signsDTO.setSignCount(0);
                    }

                    return signsDTO;
                }).collect(Collectors.toList());

                signPageDTO.setSigns(signs);

                signPageDTOs.add(signPageDTO);
            }

        }

        return signPageDTOs;
    }
}
